home *** CD-ROM | disk | FTP | other *** search
Text File | 1995-05-31 | 8.7 KB | 284 lines | [TEXT/ttxt] |
- // This may look like C code, but it is really -*- C++ -*-
- /*
- ************************************************************************
- *
- * Grayscale Image
- *
- * Perform convolutions, linear FIR filtrations
- * of an image
- *
- * The present file implements several flavors of the linear FIR filtration,
- * by rows, by columns, or a separable 2D filtration (which is filtration
- * of rows followed by filtering the columns of an image). The file also
- * contains the code for look-up table substitutions.
- *
- * The algorithm is simple, say for a 3-point FIR filter with coefficients
- * [am1,a0,ap1], the pixels of a filtered image are computed as
- * new_pixel[l] = am1*pixel[l-1] + a0*pixel[l] + ap1*pixel[l+1]
- * For row-wise filtration, pixel[l] = image(i,l) and we repeat this
- * for all rows i; In column-wise filtration, pixel[l] = image(l,j)
- * and we make image.q_ncols() that many filtration (separate for
- * each column of the image, that is).
- * When filtering window sticks out of the image, we extrapolate
- * the corresponding pixels. That is, we assume pixel[-1] = pixel[0]
- * and pixel[lmax+1] = pixel[lmax]
- *
- * All filtration is done in-place!
- *
- * $Id: conv_filter.cc,v 2.0 1995/03/17 17:20:43 oleg Exp oleg $
- *
- ************************************************************************
- */
-
- #ifdef __GNUC__
- #pragma implementation "filter.h"
- #endif
- #include "filter.h"
-
- #include <minmax.h>
-
- // Expand the templates right here...
- template class ConvKernel3<CommonDenomOne>;
- template class ConvKernel3<over_2_up>;
- template class ConvKernel3<CommonDenom>;
-
- /*
- *------------------------------------------------------------------------
- * Simple 3-point convolutions
- */
-
-
- // 3-point row-wise convolutions
- #define CONV3(Denom) \
- \
- IMAGE& FilterIt::conv_row(const ConvKernel3<Denom>& kernel) \
- { \
- if( image.ncols < 2 ) \
- image.info(), \
- _error("we need at least 3 columns for a 3-point row convolution"); \
- \
- register GRAY_SIGNED * pp = (GRAY_SIGNED *)image.pixels; \
- register GRAY_SIGNED prev_pixel; /* in the row */ \
- \
- for(GRAY_SIGNED * row_end = (GRAY_SIGNED *)image.pixels + image.ncols; \
- row_end <= (GRAY_SIGNED *)image.pixels + image.npixels; \
- row_end += image.ncols) \
- { \
- prev_pixel = *pp; /* Assume pixel[-1]=pixel[0] */ \
- \
- while(pp < row_end-1) \
- { \
- GRAY_SIGNED new_pixel = kernel.convolve(prev_pixel,pp[0],pp[1]); \
- prev_pixel = pp[0]; \
- *pp++ = new_pixel; \
- } \
- *pp++ = kernel.convolve(prev_pixel,pp[0],pp[0]); \
- } \
- assert( pp == (GRAY_SIGNED *)image.pixels + image.npixels ); \
- return image; \
- } \
- \
- /* 3-point col-wise convolution */ \
- IMAGE& FilterIt::conv_col(const ConvKernel3<Denom>& kernel) \
- { \
- if( image.nrows < 2 ) \
- image.info(), \
- _error("we need at least 3 rows for a 3-point col convolution"); \
- \
- register const int ncols = image.ncols; /* caching */ \
- const int fl_jump = image.npixels - ncols; /* between 1. and last rows */\
- register GRAY_SIGNED * pp = (GRAY_SIGNED *)image.pixels; \
- register GRAY_SIGNED prev_pixel; /* in the col */ \
- \
- GRAY_SIGNED * last_row = (GRAY_SIGNED *)image.pixels + fl_jump; \
- for(; last_row < (GRAY_SIGNED *)image.pixels + image.npixels; \
- last_row++) \
- { \
- prev_pixel = *pp; /* Assume pixel[-1]=pixel[0] */ \
- \
- for(; pp < last_row; pp += ncols) \
- { \
- GRAY_SIGNED new_pixel = kernel.convolve(prev_pixel,pp[0],pp[ncols]); \
- prev_pixel = pp[0]; \
- pp[0] = new_pixel; \
- } \
- pp[0] = kernel.convolve(prev_pixel,pp[0],pp[0]); \
- pp -= fl_jump-1; /* Jump over the next col */ \
- } \
- assert( pp == last_row - fl_jump ); \
- return image; \
- } \
-
-
- CONV3(CommonDenomOne)
- CONV3(over_2_up)
- CONV3(CommonDenom)
-
- #undef CONV3
-
- //------------------------------------------------------------------------
- // Routing filtration module
- // pick up and execute the most efficient method given input parameters
- //
- // I wish I could use templates, but theu suck! I can't declare templates
- // inside classes, w/o making an entire type templated
- // I can't help but resort to a good old c preprocessor
-
- #define CONV_ROUTER(Denom) \
- \
- IMAGE& FilterIt::conv(const ConvKernel3<Denom>& kernel,const Direction how) \
- { \
- image.is_valid(); \
- \
- switch(how) \
- { \
- case RowsAndColumns: \
- return conv_row(kernel), conv_col(kernel); \
- case Columns: \
- return conv_col(kernel); \
- case Rows: \
- return conv_row(kernel); \
- } \
- _error("There is no way we can reach here..."); \
- return image; /* just to make the compiler happy */\
- } \
-
-
- CONV_ROUTER(CommonDenomOne)
- CONV_ROUTER(over_2_up)
- CONV_ROUTER(CommonDenom)
-
- #undef CONV_ROUTER
-
- /*
- *------------------------------------------------------------------------
- * Table Look-ups
- */
-
- // Allocate an empty look-up table
- void LookupT::allocate(const GRAY _min_val, const GRAY _max_val)
- {
- assure(_max_val >= _min_val, "bad range allocating the LookUp table");
- max_val = _max_val;
- min_val = _min_val;
- no_entries = max_val - min_val + 1;
- if( no_entries > Preallocated_no )
- table = new GRAY[no_entries];
- else // Use storage preallocated within
- table = preallocated; // the object whenever possible
- }
-
-
-
- // Copy constructor
- LookupT::LookupT(const LookupT& another)
- {
- allocate(another.min_val,another.max_val);
- *this = another;
- }
-
- // It's OK to assign one LookupT to another
- // But they have to be similar
- LookupT& LookupT::operator= (const LookupT& another)
- {
- if( min_val != another.min_val || max_val != another.max_val )
- _error("Can't assign [%d,%d]-range look-up table to [%d,%d] one",
- another.min_val, another.max_val, min_val, max_val );
- assert( no_entries == another.no_entries );
- memcpy(table,another.table,sizeof(table[0])*no_entries);
- return *this;
- }
-
- // Destructor
- LookupT::~LookupT(void)
- {
- assert( table != 0 && no_entries > 0 && max_val >= min_val );
- if( table != preallocated )
- delete [] table;
- table = 0;
- }
-
-
- // Build a table for an identical pixel
- // mapping for a specified depth image
- LookupT::LookupT(const Identical id)
- {
- assure(id.depth > 0 && id.depth < 14,
- "a fishy depth to build a lookup table for");
- allocate(0,(1<<id.depth)-1);
- for(register GRAY i=min_val; i<=max_val; i++)
- table[i-min_val] = i;
- }
-
-
- // Build a table to map just one
- // particular pixel
- LookupT::LookupT(const MapTo map_one)
- {
- allocate(map_one.from,map_one.from);
- (*this)[map_one.from] = map_one.to;
- }
-
- // Build a table to reduce image depth from
- // one value to another (that is, build a
- // stripy table)
- LookupT::LookupT(const DownGRAY downgray)
- {
- assure(downgray.depth_from > 0 && downgray.depth_from < 14 &&
- downgray.depth_from >= downgray.depth_to,
- "a fishy depths to build a downgray lookup table for");
- allocate(0,(1<<downgray.depth_from)-1);
- const card downgr_factor = 1<<(downgray.depth_from-downgray.depth_to);
- register GRAY * tp = table;
- register GRAY val;
- for(val=0; tp < table + no_entries; val++)
- for(register int i=0; i<downgr_factor; i++)
- *tp++ = val;
- assert( tp == table + no_entries && val == 1<<downgray.depth_to );
- }
-
- // Convolve two Lookup tables
- LookupT& LookupT::apply(const LookupT& another,
- const FringeHandling fringe_handling)
- {
- register GRAY_SIGNED * tp;
- if( fringe_handling == CoerceFringes )
- for(tp=(GRAY_SIGNED *)table; tp < (GRAY_SIGNED *)table + no_entries; tp++)
- *tp = another[min(max(*tp,(GRAY_SIGNED)another.min_val),
- (GRAY_SIGNED)another.max_val)];
- else // Leave fringes alone
- for(tp=(GRAY_SIGNED *)table; tp < (GRAY_SIGNED *)table + no_entries; tp++)
- if( *tp >= another.min_val && *tp <= another.max_val )
- *tp = another[*tp];
- return *this;
- }
-
- // Translate image pixels according to
- // the look-up table
- IMAGE& FilterIt::translate(const LookupT& lookupt,
- const LookupT::FringeHandling fringe_handling)
- {
- register GRAY_SIGNED * pp = (GRAY_SIGNED *)image.pixels;
- if( fringe_handling == LookupT::CoerceFringes )
- while( pp < (GRAY_SIGNED *)image.pixels + image.npixels )
- {
- int offset = *pp - lookupt.min_val;
- if( offset < 0 )
- offset = 0;
- else if( offset >= lookupt.no_entries )
- offset = lookupt.no_entries - 1;
- *pp++ = lookupt.table[offset];
- }
- else // Leave fringes alone
- for(; pp < (GRAY_SIGNED *)image.pixels + image.npixels; pp++)
- {
- int offset = *pp - lookupt.min_val;
- if( offset >= 0 && offset < lookupt.no_entries )
- *pp = lookupt.table[offset];
- }
-
- return image;
- }
-
-
-